<template>
  <n-card :bordered="false">
    <n-alert :show-icon="false" class="m-b-10">
      <n-form :model="search_param" class="search-forms"
              inline
              :show-feedback="false"
              label-placement="left"
              @keydown.enter.prevent="handleSubmitSearch">
        <n-form-item label="角色名">
          <n-input v-model:value="search_param.name" placeholder="请输入角色名查询" style="width:200px"
                   clearable/>
        </n-form-item>
        <n-form-item label="状态">
          <n-select v-model:value="search_param.status" placeholder="请选择状态" style="width:130px"
                    :options="statusOptions" clearable/>
        </n-form-item>
        <n-form-item>
          <SearchButtonGroup @submit="handleSubmitSearch"
                             @reset="()=>(search_param.name=null,search_param.status=null)"/>
        </n-form-item>
      </n-form>
    </n-alert>
    <data-table ref="role_list"
                :columns="columns"
                :action-column="actionColumn"
                :row-key="(row:Recordable) => row.id"
                :params="search_param"
                list-url="role/list"
                default-serial-number
                serial-number-title="序号">
      <template #toolbarLeft>
        <n-button type="success" v-permission="['role:add']" @click="()=>(addEditRole=true)">
          <n-icon :size="18">
            <AddSharp/>
          </n-icon>
          新增
        </n-button>
      </template>
    </data-table>
  </n-card>
  <!--新增、修改角色-->
  <n-modal v-model:show="addEditRole" :title="addEditRoleForm.id?'修改角色':'新增角色'"
           :show-icon="false" :mask-closable="false" style="width: 620px"
           preset="dialog" @after-leave="closeAddEditRole">
    <n-form ref="addEditRoleFormRef"
            :model="addEditRoleForm"
            :rules="addEditRoleFormRules"
            label-placement="left"
            label-width="auto"
            require-mark-placement="left">
      <n-form-item label="角色名称" path="name">
        <n-input v-model:value="addEditRoleForm.name" placeholder="角色名称" clearable/>
      </n-form-item>
      <n-form-item label="角色描述" path="roleDescribe">
        <n-input v-model:value="addEditRoleForm.roleDescribe" type="textarea" placeholder="角色描述"
                 :autosize="{minRows: 2}"/>
      </n-form-item>
      <n-form-item label="状态" path="status">
        <n-switch :round="false" v-model:value="addEditRoleForm.status" :checked-value="1" :unchecked-value="0">
          <template #checked>
            启用
          </template>
          <template #unchecked>
            停用
          </template>
        </n-switch>
      </n-form-item>
      <n-form-item label="权限" path="permissionList">
        <div class="permission">
          <div class="all-checked">
            <n-checkbox v-model:checked="allChecked" :indeterminate="indeterminateCheckAll"
                        @update:checked="checkedAll">{{
                allChecked ? '全取消' : !allChecked && indeterminateCheckAll ? '已部分选中/全选' : '全选'
              }}
            </n-checkbox>
          </div>
          <div class="permission-list">
            <n-checkbox-group v-model:value="addEditRoleForm.permissionList" @update:value="checkPermission">
              <template v-for="item in rolePermission">
                <div class="permission-list-directory">
                  <n-checkbox :value="item.key">{{ item.label }}{{ item.children ? ':' : '' }}</n-checkbox>
                  <div class="permission-list-menu">
                    <template v-for="menu in item.children">
                      <n-checkbox :value="menu.key">{{ menu.label }}{{ menu.children ? ':' : '' }}</n-checkbox>
                      <div class="permission-list-menu-act">
                        <n-space>
                        <template v-for="act in menu.children">
                          <n-checkbox :value="act.key">{{ act.label }}</n-checkbox>
                        </template>
                        </n-space>
                      </div>
                    </template>
                  </div>
                </div>
              </template>
            </n-checkbox-group>
          </div>
        </div>
      </n-form-item>
    </n-form>
    <template #action>
      <n-space>
        <n-button @click="()=>(addEditRole=false)">取消</n-button>
        <n-button type="primary" :loading="handelSubmitRoleLoading" @click="handelSubmitRole">确定</n-button>
      </n-space>
    </template>
  </n-modal>
</template>

<script setup lang="ts">
import {ref, reactive, h, onMounted,computed} from 'vue';
import SearchButtonGroup from '@components/SearchButtonGroup';
import DataTable from '@components/DataTable'
import DataTableAction from '@components/DataTable/src/components/DataTableAction';
import {NTag, NPopconfirm, FormInst, useMessage, SelectOption} from 'naive-ui'
import {AddSharp} from '@vicons/material';
import {delRequest, getRequest, postPutRequest} from '@/api/data.ts';
import {flattenTreeData,isSubset,findMenuParent} from '@/util';

interface Permission {
  label: string,
  key: number | string,
  type: number | string,
  children: Permission[]
}

interface RoleForm {
  id: number,
  name: string,
  roleDescribe: string,
  status: number,
  permissionList: (string|number)[]
}
const message = useMessage()

const search_param = ref({
  name: null,
  status: null
})
//状态选项
const statusOptions = ref<SelectOption[]>([
  {
    label: '正常',
    value: 1
  },
  {
    label: '停用',
    value: 0
  },
])
//数据表ref
const role_list = ref<any>();
const columns = ref([
  {
    title: "名称",
    key: "name",
    width: 180,
  },
  {
    title: "状态",
    key: "status",
    width: 80,
    render(row: Recordable) {
      return h(NTag,
          {
            type: row.status == 1 ? 'success' : 'error',
          },
          {
            default: () => (row.statusText),
          }
      );
    }
  },
  {
    title: "描述",
    key: "roleDescribe",
    width: 200,
  },
  {
    title: "权限",
    key: "roles",
    render(row: Recordable) {
      const permission: any[] = row.permissionList;
      let permission_text: any;
      if (permission.length) {
        for (let i = 0; i < permission.length; i++) {
          if (i == 0) {
            permission_text = permission[i]['label'];
          } else {
            permission_text += "," + permission[i]['label']
          }
          let child_title = "";
          if (permission[i]['children'] && permission[i]['children'].length) {
            child_title += ":[";
            for (let j = 0; j < permission[i]['children'].length; j++) {
              let act_title = "";
              if (j === 0) {
                child_title += permission[i]['children'][j]['label']
              } else {
                child_title += "," + permission[i]['children'][j]['label']
              }
              if (permission[i]['children'][j]['children'] && permission[i]['children'][j]['children'].length) {
                act_title += ":[";
                for (let k = 0; k < permission[i]['children'][j]['children'].length; k++) {
                  if (k === 0) {
                    act_title += permission[i]['children'][j]['children'][k]['label']
                  } else {
                    act_title += "," + permission[i]['children'][j]['children'][k]['label']
                  }
                }
                act_title += "]";
              }
              child_title += act_title;
            }
            child_title += "]";
          }
          permission_text += child_title
        }
      }
      return h('span', null, {
        default: () => (permission_text),
      })
    },
  },
  {
    title: "创建时间",
    key: "createTime",
    width: 175,
  },
  {
    title: "更新时间",
    key: "updateTime",
    width: 175,
  },
]);
//操作列
const actionColumn = reactive({
  width: 120,
  title: "操作",
  key: "action",
  fixed: "right",
  render(record: Recordable) {
    return h(DataTableAction, {
      actions: [
        {
          label: '修改',
          onClick: handleEdit.bind(null, record),
          type: 'primary',
          permission: ['role:edit']
        },
        {
          label: '删除',
          type: 'error',
          popConfirm: NPopconfirm,
          onPositiveClick: handleDelete.bind(null, record),
          confirm: {
            text: '确定删除当前角色吗？',
          },
          permission: ['role:del']
        },
      ],
    });
  },
});
//新增、修改角色
const rolePermission = ref<Permission[]>([]);//菜单权限数据  树形结构
const flattenRolePermission=ref<Permission[]>([]);//菜单权限数据 转换成一维数组的
const addEditRole = ref<boolean>(false);
const addEditRoleFormRef = ref<FormInst | null>(null);
const handelSubmitRoleLoading = ref<boolean>(false);
//全选
const allChecked = ref<boolean>(false);
const addEditRoleForm = ref<RoleForm>({
  id: 0,
  name: '',
  roleDescribe: '',
  status: 1,
  permissionList: []
})
const addEditRoleFormRules = ref({
  name: {
    required: true,
    trigger: ['blur', 'input'],
    message: '请填写角色名称'
  },
  roleDescribe: {
    required: true,
    trigger: ['blur', 'input'],
    message: '请填写角色描述'
  },
  permissionList: {
    type: 'array',
    required: true,
    trigger: 'change',
    message: '请选择权限'
  }
});
//部分选
const indeterminateCheckAll = computed(() => {
  const permission: number = flattenRolePermission.value.length;
  const selectedPermission: number = addEditRoleForm.value.permissionList.length;
  if (permission == selectedPermission) {
    allChecked.value = true;
    return false
  } else if (selectedPermission != 0 && selectedPermission != permission) {
    allChecked.value = false;
    return true
  } else {
    return false
  }
})
//全选、取消
const checkedAll = (checked: boolean) => {
  if (checked) {
    addEditRoleForm.value.permissionList = flattenRolePermission.value.map((item: Permission) => {
      return item.key
    });
  } else {
    addEditRoleForm.value.permissionList = [];
  }
}
const checkPermission = (_value:string|number|any,meta: {actionType: 'check' | 'uncheck',value: string | number| any}) => {
  const parentIds: string[] | number[] | undefined = findMenuParent(rolePermission.value, meta.value);
  let newPermissionList: any;
  if (meta.actionType === 'check') {
    if (parentIds?.length) {
      newPermissionList = reductionPermission(parentIds);
    } else {
      newPermissionList = reductionPermission(findChildrenIds(rolePermission.value, meta.value));
    }
  } else {
    if (!parentIds?.length) {
      const arr:any[] = findChildrenIds(rolePermission.value, meta.value);
      newPermissionList = addEditRoleForm.value.permissionList.filter((x:any) =>{return !arr.includes(x)});
    } else {
      //如果 按钮选中的情况下无法取消菜单
      const arr = flattenRolePermission.value.filter((item: Permission) => {
        return item.key == meta.value
      });
      if (arr[0].type == 2) {
        const childIds = findChildrenIds(arr, meta.value).filter(e => e !== meta.value);
        if (childIds.length && isSubset(addEditRoleForm.value.permissionList, childIds)) {
          newPermissionList=[...addEditRoleForm.value.permissionList,meta.value];
        } else {
          //没有选中按钮并取消菜单时，检查当前目录的其他菜单是否选中状态、若没有，取消当前目录选中状态
          const childrenIds = findChildrenIds(rolePermission.value, parentIds[0]).filter(e => e !== meta.value && e !== parentIds[0]);
          if (!isSubset(addEditRoleForm.value.permissionList, childrenIds)) {
            newPermissionList = addEditRoleForm.value.permissionList.filter(e => e !== parentIds[0])
          }else {
            newPermissionList=addEditRoleForm.value.permissionList;
          }
        }
      }else {
        newPermissionList=addEditRoleForm.value.permissionList;
      }
    }
  }
  addEditRoleForm.value.permissionList = newPermissionList;
}
const findChildrenIds = (permission: Permission[], key:string|number): number[] | string[] => {
  const child = permission.filter((item: Permission) => {
    return item.key == key
  });
  return flattenTreeData(child).map((item: Permission) => {
    return item.key
  });
}
//去重权限组
const reductionPermission = (data: number[] | string[]): number[] | string[] | any[] => {
  const newPermissionList = new Map();
  return [...addEditRoleForm.value.permissionList, ...data].filter((item:number|string) => !newPermissionList.has(item) && newPermissionList.set(item,true));
}
//修改
const handleEdit = (record: Recordable) => {
  addEditRoleForm.value.id = record.id;
  addEditRoleForm.value.name = record.name;
  addEditRoleForm.value.roleDescribe = record.roleDescribe;
  addEditRoleForm.value.status = record.status;
  addEditRoleForm.value.permissionList = record.permission;
  addEditRole.value = true;
}
//删除
const handleDelete = (record: Recordable) => {
  delRequest('role/del',record.id).then((result:ResponseResult)=>{
    if (result.status === 0 && result.code === 200) {
      message.success(result.message, {
        duration: 1500,
        onLeave() {
          role_list.value.reload();
        }
      })
    } else {
      message.error(result.message);
    }
  })
}
//查询
const handleSubmitSearch = () => {
  role_list.value.searchListData();
}
const closeAddEditRole = () => {
  addEditRoleForm.value.id = 0;
  addEditRoleForm.value.name = '';
  addEditRoleForm.value.roleDescribe = '';
  addEditRoleForm.value.status = 1;
  addEditRoleForm.value.permissionList = [];
  allChecked.value = false;
  handelSubmitRoleLoading.value = false;
}
//提交数据
const handelSubmitRole = (e: MouseEvent) => {
  e.preventDefault();
  addEditRoleFormRef.value?.validate((errors) => {
    if (!errors) {
      handelSubmitRoleLoading.value = true;
      const url = addEditRoleForm.value.id ? 'role/edit' : 'role/add';
      postPutRequest(url,addEditRoleForm.value,addEditRoleForm.value.id ? 'PUT' : 'POST').then((result:ResponseResult)=>{
        if (result.code === 200) {
          message.success(result.message, {
            duration: 1500,
            onLeave() {
              addEditRole.value = false;
              if (addEditRoleForm.value.id) {
                role_list.value.reload();
              } else {
                role_list.value.searchListData();
              }
            }
          })
        } else {
          message.error(result.message);
          handelSubmitRoleLoading.value = false;
        }
      })
    }
  })
}
//获取菜单权限数据
const getPermission = async () => {
  await getRequest("menu/get_permission").then((result: ResponseResult) => {
    const {data}=result;
    rolePermission.value=data;
    //树形结构的数据转换成一维数组
    flattenRolePermission.value=flattenTreeData(data);
  })
}
onMounted(() => {
  getPermission();
})
</script>

<style scoped lang="scss">
.permission {
  width: 100%;
  margin-top: 5px;

  &-list {
    max-height: 380px;
    overflow: auto;

    &-menu, &-menu-act {
      padding-left: 25px;
    }
  }
}
</style>
